/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "TextBox.h"

#include "OgreBorderPanelOverlayElement.h"
#include "OgreFontManager.h"
#include "OgrePanelOverlayElement.h"

namespace FF
{
	TextBox::TextBox(const Ogre::String& name, const Style& style, const Ogre::DisplayString& caption, Ogre::Real width, Ogre::Real height)
		: mStyle(style)
	{
		mElement = Ogre::OverlayManager::getSingleton().createOverlayElementFromTemplate(mStyle.templateName, "BorderPanel", name);
		mElement->setWidth(width);
		mElement->setHeight(height);
		Ogre::OverlayContainer* container = (Ogre::OverlayContainer*)mElement;
		mTextArea = (Ogre::TextAreaOverlayElement*)container->getChild(GetName() + "/TextBoxText");
		mCaptionBar = (Ogre::BorderPanelOverlayElement*)container->getChild(GetName() + "/TextBoxCaptionBar");
		mCaptionBar->setWidth(width - 4);
		mCaptionTextArea = (Ogre::TextAreaOverlayElement*)mCaptionBar->getChild(mCaptionBar->getName() + "/TextBoxCaption");
		SetCaption(caption);
		mScrollTrack = (Ogre::BorderPanelOverlayElement*)container->getChild(GetName() + "/TextBoxScrollTrack");
		mScrollHandle = (Ogre::PanelOverlayElement*)mScrollTrack->getChild(mScrollTrack->getName() + "/TextBoxScrollHandle");
		mScrollHandle->hide();
		mDragging = false;
		mScrollPercentage = 0;
		mStartingLine = 0;
		mPadding = 15;
		mText = "";
		RefitContents();
	}

	void TextBox::SetPadding(Ogre::Real padding)
	{
		mPadding = padding;
		RefitContents();
	}

	Ogre::Real TextBox::GetPadding()
	{
		return mPadding;
	}

	const Ogre::DisplayString& TextBox::GetCaption()
	{
		return mCaptionTextArea->getCaption();
	}

	void TextBox::SetCaption(const Ogre::DisplayString& caption)
	{
		mCaptionTextArea->setCaption(caption);
	}

	const Ogre::DisplayString& TextBox::GetText()
	{
		return mText;
	}

	void TextBox::SetText(const Ogre::DisplayString& text)
	{
		mText = text;
		mLines.clear();

		Ogre::Font* font = (Ogre::Font*)Ogre::FontManager::getSingleton().getByName(mTextArea->getFontName()).getPointer();

		Ogre::String current = text.asUTF8();
		bool firstWord = true;
		unsigned int lastSpace = 0;
		unsigned int lineBegin = 0;
		Ogre::Real lineWidth = 0;
		Ogre::Real rightBoundary = mElement->getWidth() - 2 * mPadding + mScrollTrack->getLeft() + 10;

		for (unsigned int i = 0; i < current.length(); i++)
		{
			if (current[i] == ' ')
			{
				if (mTextArea->getSpaceWidth() != 0) lineWidth += mTextArea->getSpaceWidth();
				else lineWidth += font->getGlyphAspectRatio(' ') * mTextArea->getCharHeight();
				firstWord = false;
				lastSpace = i;
			}
			else if (current[i] == '\n')
			{
				firstWord = true;
				lineWidth = 0;
				mLines.push_back(current.substr(lineBegin, i - lineBegin));
				lineBegin = i + 1;
			}
			else
			{
				// use glyph information to calculate line width
				lineWidth += font->getGlyphAspectRatio(current[i]) * mTextArea->getCharHeight();
				if (lineWidth > rightBoundary)
				{
					if (firstWord)
					{
						current.insert(i, "\n");
						i = i - 1;
					}
					else
					{
						current[lastSpace] = '\n';
						i = lastSpace - 1;
					}
				}
			}
		}

		mLines.push_back(current.substr(lineBegin));

		unsigned int maxLines = GetHeightInLines();

		if (mLines.size() > maxLines)     // if too much text, filter based on scroll percentage
		{
			mScrollHandle->show();
			FilterLines();
		}
		else       // otherwise just show all the text
		{
			mTextArea->setCaption(current);
			mScrollHandle->hide();
			mScrollPercentage = 0;
			mScrollHandle->setTop(0);
		}
	}

	void TextBox::SetTextAlignment(Ogre::TextAreaOverlayElement::Alignment ta)
	{
		if (ta == Ogre::TextAreaOverlayElement::Left) mTextArea->setHorizontalAlignment(Ogre::GHA_LEFT);
		else if (ta == Ogre::TextAreaOverlayElement::Center) mTextArea->setHorizontalAlignment(Ogre::GHA_CENTER);
		else mTextArea->setHorizontalAlignment(Ogre::GHA_RIGHT);
		RefitContents();
	}

	void TextBox::ClearText()
	{
		SetText("");
	}

	void TextBox::AppendText(const Ogre::DisplayString& text)
	{
		SetText(GetText() + text);
	}

	void TextBox::RefitContents()
	{
		mScrollTrack->setHeight(mElement->getHeight() - mCaptionBar->getHeight() - 20);
		mScrollTrack->setTop(mCaptionBar->getHeight() + 10);

		mTextArea->setTop(mCaptionBar->getHeight() + mPadding - 5);
		if (mTextArea->getHorizontalAlignment() == Ogre::GHA_RIGHT) mTextArea->setLeft(-mPadding + mScrollTrack->getLeft());
		else if (mTextArea->getHorizontalAlignment() == Ogre::GHA_LEFT) mTextArea->setLeft(mPadding);
		else mTextArea->setLeft(mScrollTrack->getLeft() / 2);

		SetText(GetText());
	}

	void TextBox::SetScrollPercentage(Ogre::Real percentage)
	{
		mScrollPercentage = Ogre::Math::Clamp<Ogre::Real>(percentage, 0, 1);
		mScrollHandle->setTop((Ogre::Real)(int)(percentage * (mScrollTrack->getHeight() - mScrollHandle->getHeight())));
		FilterLines();
	}

	Ogre::Real TextBox::GetScrollPercentage()
	{
		return mScrollPercentage;
	}

	unsigned int TextBox::GetHeightInLines()
	{
		return (unsigned int) ((mElement->getHeight() - 2 * mPadding - mCaptionBar->getHeight() + 5) / mTextArea->getCharHeight());
	}

	void TextBox::_OnCursorPressed(const Ogre::Vector2& cursorPos)
	{
		if (!mScrollHandle->isVisible()) return;   // don't care about clicks if text not scrollable

		Ogre::Vector2 co = Widget::CursorOffset(mScrollHandle, cursorPos);

		if (co.squaredLength() <= 81)
		{
			mDragging = true;
			mDragOffset = co.y;
		}
		else if (Widget::IsCursorOver(mScrollTrack, cursorPos))
		{
			Ogre::Real newTop = mScrollHandle->getTop() + co.y;
			Ogre::Real lowerBoundary = mScrollTrack->getHeight() - mScrollHandle->getHeight();
			mScrollHandle->setTop((Ogre::Real)Ogre::Math::Clamp<int>((int)newTop, 0, (int)lowerBoundary));

			// update text area contents based on new scroll percentage
			mScrollPercentage = Ogre::Math::Clamp<Ogre::Real>(newTop / lowerBoundary, 0, 1);
			FilterLines();
		}
	}

	void TextBox::_OnCursorReleased(const Ogre::Vector2& cursorPos)
	{
		mDragging = false;
	}

	void TextBox::_OnCursorMoved(const Ogre::Vector2& cursorPos)
	{
		if (mDragging)
		{
			Ogre::Vector2 co = Widget::CursorOffset(mScrollHandle, cursorPos);
			Ogre::Real newTop = mScrollHandle->getTop() + co.y - mDragOffset;
			Ogre::Real lowerBoundary = mScrollTrack->getHeight() - mScrollHandle->getHeight();
			mScrollHandle->setTop((Ogre::Real)Ogre::Math::Clamp<int>((int)newTop, 0, (int)lowerBoundary));

			// update text area contents based on new scroll percentage
			mScrollPercentage = Ogre::Math::Clamp<Ogre::Real>(newTop / lowerBoundary, 0, 1);
			FilterLines();
		}
	}

	void TextBox::_OnFocusLost()
	{
		mDragging = false;  // stop dragging if cursor was lost
	}

	void TextBox::FilterLines()
	{
		Ogre::String shown = "";
		unsigned int maxLines = GetHeightInLines();
		unsigned int newStart = (unsigned int) (mScrollPercentage * (mLines.size() - maxLines) + 0.5);

		mStartingLine = newStart;

		for (unsigned int i = 0; i < maxLines; i++)
		{
			shown += mLines[mStartingLine + i] + "\n";
		}

		mTextArea->setCaption(shown);    // show just the filtered lines
	}
}
